package model

import (
	"database/sql"
	"encoding/json"
	"time"
	"errors"
	"net/http"
	"io/ioutil"
	"api/util"
	"github.com/dop251/goja"
)

type Charge struct {
	Id          		int    	`json:"id"`
	Phone       		string 	`json:"phone"`
	Amount       		int    	`json:"amount"`
	Name        		string 	`json:"name"`
	Bank        		string 	`json:"bank"`
	BankNo      		string	`json:"bank_no"`
	Status      		int    	`json:"status"`
	PromotionOfficerId 	int 	`json:"promotion_officer_id"`
	CreatedDate 		int    	`json:"created_date"`
	UpdatedDate 		int    	`json:"updated_date"`
}

func redisCharge() (ret []*Charge, err error) {
	err = rdb.Del(ctx, "charge").Err()
	if err != nil {
		return ret, err
	}

	rows, err := db.Query("SELECT * FROM charge ORDER BY updated_date DESC, status DESC")
	if err != nil {
		return ret, err
	}

	defer rows.Close()

	for rows.Next() {
		c := Charge{}
		err := rows.Scan(&c.Id, &c.Phone, &c.Amount, &c.Name, &c.Bank, &c.BankNo, &c.Status, &c.PromotionOfficerId, &c.CreatedDate, &c.UpdatedDate)
		if err != nil {
			return ret, err
		}

		ret = append(ret, &c)

		jsonStu, err := json.Marshal(c)
		if err != nil {
			return ret, err
		}
		err = rdb.RPush(ctx, "charge", jsonStu).Err()
		if err != nil {
			return ret, err
		}
	}

	return ret, nil
}

func (cg *Charge) QueryCharge(status string, phone string) (ret []*Charge, err error) {
	sql1 := "SELECT * FROM charge WHERE"
	if len(status) > 0 && len(phone) == 0 {
		sql1 += " status = " + status
	} else if len(status) == 0 && len(phone) > 0 {
		sql1 += " phone = " + phone
	} else {
		sql1 += " status = " + status + " AND phone = " + phone
	}

	sql1 += " ORDER BY updated_date DESC"

	rows, err := db.Query(sql1)
	if err != nil {
		return ret, err
	}

	for rows.Next() {
		c := Charge{}
		err := rows.Scan(&c.Id, &c.Phone, &c.Amount, &c.Name, &c.Bank, &c.BankNo, &c.Status, &c.PromotionOfficerId, &c.CreatedDate, &c.UpdatedDate)
		if err != nil {
			return ret, err
		}
		ret = append(ret, &c)
	}

	defer rows.Close()

	return ret, nil
}

func (cg *Charge) GetCharge() (ret []*Charge, err error) {
	vals, err := rdb.LRange(ctx, "charge", 0, -1).Result()
	if err != nil {
		return nil, err
	}

	if len(vals) > 0 {
		for _, v := range vals {
			c := Charge{}
			err := json.Unmarshal([]byte(v), &c)
			if err != nil {
				return ret, err
			}

			ret = append(ret, &c)
		}

		return ret, nil
	}

	ret, err = redisCharge()
	if err != nil {
		return ret, err
	}

	return ret, nil
}

func (cg *Charge) CreateCharge(phone string, amount int) error {
	stmt, err := db.Prepare("SELECT id, status FROM white_list WHERE phone=?")
	if err != nil {
		return err
	}

	var wid, wst int
	err = stmt.QueryRow(phone).Scan(&wid, &wst)
	if err != nil && err != sql.ErrNoRows{
		return err
	}

	if err == sql.ErrNoRows {
		return errors.New("用户不在白名单内")
	}

	if wst != 2 {
		if wst == 1 {
			return errors.New("此白名单已被驳回")
		} else if wst == 0 {
			return errors.New("白名单待处理")
		} else {
			return errors.New("错误状态码")
		}
	}

	stmt0, err := db.Prepare("SELECT id, status FROM promotion_officer WHERE phone=?")
	if err != nil {
		return err
	}

	var pid, pst int
	err = stmt0.QueryRow(phone).Scan(&pid, &pst)
	if err != nil {
		return err
	}
	if pst == 0 {
		return errors.New("因违规操作，您的账号暂时被锁定")
	}

	params := util.GetParams(`{"uuid":"` + util.MERID + phone + `"}`)
	resp, err := http.Post(util.ADDR + `/api/maker_auth_info.api`, "application/json", params)
	if err != nil {
		return err		
	}
	defer resp.Body.Close()

	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	respString := string(respBody)
	respMap := make(map[string]interface{})

	err = json.Unmarshal([]byte(respString), &respMap)
	if err != nil {
		return err
	}

	statusCode := int(respMap["status_code"].(float64))
	if statusCode != 200 {
		return errors.New("调用第三方接口失败 -> 异常状态码")
	}

	isAuth := int(respMap["data"].(map[string]interface{})["is_auth"].(float64))
	isSign := int(respMap["data"].(map[string]interface{})["is_sign"].(float64))

	chg := Charge{}
	if isAuth == 2 && isSign == 1 {
		chg.Phone = respMap["data"].(map[string]interface{})["mobile"].(string)
		chg.Amount = amount
		chg.Name = respMap["data"].(map[string]interface{})["name"].(string)
		chg.BankNo = respMap["data"].(map[string]interface{})["bank_card_no"].(string)
		chg.Status = 0
		chg.PromotionOfficerId = pid
	} else if isAuth != 2 || isSign != 1 {
		return errors.New("用户未完成银行卡绑定")
	} else {
		return errors.New("调用第三方接口失败 -> /api/maker_auth_info.api")
	}

	jsFile := "./script/bankCardAttribution.js"
	bytes, err := ioutil.ReadFile(jsFile)
	if err != nil {
        return err
    }
	script := string(bytes)
    vm := goja.New()
    _, err = vm.RunString(script)
    if err != nil {
        return err
	}
    var fn func(string) string
    err = vm.ExportTo(vm.Get("bankCardAttribution"), &fn)
    if err != nil {
        return err
	}

	chg.Bank = fn(chg.BankNo)

	stmt1, err := db.Prepare("INSERT charge SET phone=?, amount=?, name=?, bank=?, bank_no=?, status=?, promotion_officer_id=?, created_date=?, updated_date=?")
	if err != nil {
		return err
	}

	_, err = stmt1.Exec(chg.Phone, chg.Amount, chg.Name, chg.Bank, chg.BankNo, 0, pid, time.Now().Unix(), time.Now().Unix())
	if err != nil {
		return err
	}

	_, err = redisCharge()
	if err != nil {
		return err
	}

	return nil
}

func chargeApproval(id int, status int) error {
	tx, err := db.Begin()
	if err != nil {
		return err
	}

	stmt, err := tx.Prepare("SELECT amount, promotion_officer_id FROM charge WHERE id=?")
	if err != nil {
		tx.Rollback()
		return err
	}

	var amount, pid int
	err = stmt.QueryRow(id).Scan(&amount, &pid)
	if err != nil {
		tx.Rollback()
		return err
	}

	stmt1, err := tx.Prepare("SELECT withdrawable, income FROM promotion_officer WHERE id=?")
	if err != nil {
		tx.Rollback()
		return err
	}

	var withdrawable, income int
	err = stmt1.QueryRow(pid).Scan(&withdrawable, &income)
	if err != nil {
		tx.Rollback()
		return err
	}

	withdrawable += amount
	income += amount

	stmt3, err := tx.Prepare("UPDATE promotion_officer SET withdrawable=?, income=?, updated_date=? WHERE id=?")
	if err != nil {
		tx.Rollback()
		return err
	}

	_, err = stmt3.Exec(withdrawable, income, time.Now().Unix(), pid)
	if err != nil {
		tx.Rollback()
		return err
	}

	stmt4, err := db.Prepare("UPDATE charge SET status=?, updated_date=? WHERE id=?")
	if err != nil {
		return err
	}

	_, err = stmt4.Exec(status, time.Now().Unix(), id)
	if err != nil {
		return err
	}

	tx.Commit()

	_, err = redisPromotionOfficer()
	if err != nil {
		return err
	}

	_, err = redisCharge()
	if err != nil {
		return err
	}

	return nil
}

func chargeReject(id int, status int) error {
	stmt, err := db.Prepare("UPDATE charge SET status=?, updated_date=? WHERE id=?")
	if err != nil {
		return err
	}

	_, err = stmt.Exec(status, time.Now().Unix(), id)
	if err != nil {
		return err
	}

	_, err = redisCharge()
	if err != nil {
		return err
	}

	return nil
}

func (cg *Charge) UpdateCharge(id int, status int) error {
	if status == 1 {
		err := chargeReject(id, status)
		if err != nil {
			return err
		}
		return nil
	} else if status == 2 {
		err := chargeApproval(id, status)
		if err != nil {
			return err
		}
		return nil
	}

	return errors.New("参数status错误！")
}
